// AssitTreeNode.cpp : implementation file
//

#include "stdafx.h"
#include "assit.h"
#include "AssitTreeNode.h"
#include "CWEdit.h"
#include "CWCheckbox.h"
#include "assitLabel.h"
#include "assitView.h"

#define SPACE_HORIZON 3
// CWaiTreeNode

IMPLEMENT_DYNAMIC(CWaiTreeNode, CWnd)

CWaiTreeNode::CWaiTreeNode(CWaiTreeNode* pParent)
{
	m_pCheckBox = NULL;
	m_pLabel = NULL;
	m_pEdit = NULL;
	m_pNode = NULL;
	m_bFold = FALSE;
	m_pParent = pParent;
}

CWaiTreeNode::~CWaiTreeNode()
{
	for(int i=0; i<m_children.GetCount(); i++)
	{
		CWaiTreeNode* pChildNode = (CWaiTreeNode*)m_children.GetAt(i);
		delete pChildNode;
	}

	if(m_pCheckBox && m_pCheckBox->m_hWnd)
		m_pCheckBox->DestroyWindow();
	delete m_pCheckBox;
	if(m_pLabel && m_pLabel->m_hWnd)
		m_pLabel->DestroyWindow();
	delete m_pLabel;
	if(m_pEdit && m_pEdit->m_hWnd)
		m_pEdit->DestroyWindow();
	delete m_pEdit;
}

CWaiTreeNode* CWaiTreeNode::AddChildNode()
{
	CWaiTreeNode* pNode = new CWaiTreeNode(this);
	m_children.Add(pNode);
	return pNode;
}

CWaiTreeNode* CWaiTreeNode::AddChildNode(XMLNode* node)
{
	CWaiTreeNode* pNode = new CWaiTreeNode(this);
	pNode->setTreeNode(node);
	m_children.Add(pNode);
	return pNode;
}

CWaiTreeNode* CWaiTreeNode::GetChildNode(int index)
{
	return m_children.GetAt(index);
}

void CWaiTreeNode::InsertAfterChild(CWaiTreeNode* pTreeNode, int index)
{
	pTreeNode->m_pParent = this;
	if((index+1) >= m_children.GetCount())
		m_children.Add(pTreeNode);
	else
		m_children.InsertAt(index+1,pTreeNode);
}

CWaiTreeNode* CWaiTreeNode::InsertSibling()
{
	for(int i=0; i<m_pParent->CoundChildNodes(); i++)
	{
		if(this == m_pParent->GetChildNode(i))
		{
			CWaiTreeNode* pNode = new CWaiTreeNode(m_pParent);
			m_pParent->InsertAfterChild(pNode, i);
			return pNode;
		}
	}
	return NULL;
}

int CWaiTreeNode::CoundChildNodes()
{
	return m_children.GetCount();
}

void CWaiTreeNode::RemoveChild(CWaiTreeNode* pChildNode, BOOL bRelease)
{
	for(int i=0; i<m_children.GetCount(); i++)
	{
		if(pChildNode == m_children.GetAt(i))
		{
			if(bRelease)
			{
				pChildNode->setTreeNode(NULL);
				pChildNode->DestroyWindow();
				delete pChildNode;
			}
			m_children.RemoveAt(i);
			return;
		}
	}
}

void CWaiTreeNode::RemoveSelfFromTree()
{
	if(!m_pParent) return;
	m_pParent->RemoveChild(this);
}

CWaiTreeNode* CWaiTreeNode::PreviousSibling()
{
	if(!m_pParent) return NULL;
	CWaiTreeNode* pPrevSibling = NULL;
	for(int i=0; i<m_pParent->CoundChildNodes(); i++)
	{
		CWaiTreeNode* pTreeNode = m_pParent->GetChildNode(i);
		if(this == pTreeNode)
			return pPrevSibling;
		pPrevSibling = pTreeNode;
	}
	return NULL;
}

CWaiTreeNode* CWaiTreeNode::NextSibing()
{
	if(!m_pParent) return NULL;
	int i=0;
	for(i=0; i<m_pParent->CoundChildNodes(); i++)
	{
		CWaiTreeNode* pTreeNode = m_pParent->GetChildNode(i);
		if(this == pTreeNode)
			break;
	}
	return i>=m_pParent->CoundChildNodes()-1?NULL:m_pParent->GetChildNode(i+1);
}

CWaiTreeNode* CWaiTreeNode::InsertEndChild(CWaiTreeNode* pTreeNode)
{
	CWaiTreeNode* pParent = pTreeNode->GetParentNode();
	if(pParent)
		pParent->RemoveChild(pTreeNode, FALSE);
	InsertAfterChild(pTreeNode, m_children.GetCount());
	return pTreeNode;
}

CWaiTreeNode* CWaiTreeNode::InsertSibling(CWaiTreeNode* pTreeNode)
{
	CWaiTreeNode* pParent = pTreeNode->GetParentNode();
	if(pParent)
		pParent->RemoveChild(pTreeNode, FALSE);

	CWaiTreeNode* pGrandPa = pParent->GetParentNode();
	for(int i=0; i<pGrandPa->CoundChildNodes(); i++)
	{
		if(pParent == pGrandPa->GetChildNode(i))
		{
			pGrandPa->InsertAfterChild(pTreeNode, i);
			break;
		}
	}
	return pTreeNode;
}

// CWaiTreeNode drawing

void CWaiTreeNode::OnDraw(CDC* pDC)
{
// TODO: add draw code here
/*
	CRect rect = m_rect;
	pDC->Draw3dRect(&m_rect, RGB(0,0,0), RGB(0,0,0));
	CBrush brush;
	brush.CreateSolidBrush(RGB(0,0,255));
	pDC->FillRect(&m_rect, &brush);

	pDC->SetBkColor(RGB(0,0,255));
	if(!m_title.IsEmpty())
		pDC->DrawText(m_title, &rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
*/
}


// CWaiTreeNode diagnostics

#ifdef _DEBUG
void CWaiTreeNode::AssertValid() const
{
}

#ifndef _WIN32_WCE
void CWaiTreeNode::Dump(CDumpContext& dc) const
{
}
#endif
#endif //_DEBUG


// CWaiTreeNode message handlers
BEGIN_MESSAGE_MAP(CWaiTreeNode, CWnd)
	ON_WM_CREATE()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()


void CWaiTreeNode::UpdateLabel()
{
#if 0
	CString string;
	string.Format(L".%i", m_depth+1);
	CString	csLabel = string;
	CWEdit* pParent = m_pParent;
	while(pParent)
	{
		string.Format(L".%i",pParent->m_depth+1);
		csLabel = string+csLabel;
		pParent = pParent->m_pParent;
	}
	csLabel = csLabel.Right(csLabel.GetLength() - 1);
	if(m_pLabel)
		m_pLabel->SetWindowText(csLabel);
#endif
}

void CWaiTreeNode::UpdateWindow()
{
	if(m_pCheckBox)
	{
		m_pCheckBox->UpdateWindow();
		m_pCheckBox->ShowWindow(SW_HIDE);
		m_pCheckBox->ShowWindow(SW_SHOW);
	}
	if(m_pEdit)
	{
		m_pEdit->UpdateWindow();
		m_pEdit->ShowWindow(SW_HIDE);
		m_pEdit->ShowWindow(SW_SHOW);
	}
	if(m_pLabel)
	{
		m_pLabel->UpdateWindow();
		m_pLabel->ShowWindow(SW_HIDE);
		m_pLabel->ShowWindow(SW_SHOW);
	}
	CWnd::UpdateWindow();
}

void CWaiTreeNode::setLabel(const char* label)
{
	int nChar = MultiByteToWideChar(CP_UTF8, 0, label, strlen(label), NULL, 0);
	wchar_t* wchs = new wchar_t[nChar+1];
	wchs[nChar] = 0;
	MultiByteToWideChar(CP_UTF8, 0, label, strlen(label), wchs, nChar);

	if(m_pLabel)
	{
		m_pLabel->SetWindowTextW(wchs);
		m_pLabel->AutoSize(CString(wchs));
	}
	delete[] wchs;
}

CString CWaiTreeNode::getContent()
{
	CString wText;
	if(m_pEdit)
	{
		m_pEdit->GetWindowTextW(wText);
	}
	return wText;
}

void CWaiTreeNode::setContent(const char* label)
{
	int nChar = MultiByteToWideChar(CP_UTF8, 0, label, strlen(label), NULL, 0);
	wchar_t* wchs = new wchar_t[nChar+1];
	wchs[nChar] = 0;
	MultiByteToWideChar(CP_UTF8, 0, label, strlen(label), wchs, nChar);

	if(m_pEdit)
	{
		CString wText;
		m_pEdit->GetWindowTextW(wText);
		if(!wText.IsEmpty() && wText == CString(wchs))
		{
			//Do nothing if equal. Because SetWindowTextW will reset the cursor position to head.
			delete[] wchs;
			return;
		}

		m_pEdit->SetWindowTextW(wchs);
		m_pEdit->AutoSize(CString(wchs));
	}
	delete[] wchs;

}

void CWaiTreeNode::setCheck(BOOL bCheck, BOOL recur, BOOL isSave)
{
	if(recur)
	{
		for(int i=0; i<m_children.GetCount(); i++)
		{
			CWaiTreeNode* pTreeNode = (CWaiTreeNode*)m_children.GetAt(i);
			pTreeNode->setCheck(bCheck);
		}

		//Child is unchecked, then parents must be unchecked too.
		if(!bCheck)
		{
			CWaiTreeNode* pParent = m_pParent;
			while(pParent)
			{
				pParent->setCheck(FALSE, FALSE, isSave);
				pParent = pParent->GetParentNode();
			}
		}
	}
	if(m_pCheckBox && m_pCheckBox->m_hWnd)
	{
		m_pCheckBox->SetCheck(bCheck?BST_CHECKED:BST_UNCHECKED);
		if(bCheck)
			setTextColor(RGB(0,200,0));
		else
			setTextColor(RGB(0,0,0));
	}
	if(isSave)
	{
		XMLElement* elment = m_pNode->ToElement();
		if(elment)
			elment->SetAttribute("checked", bCheck);
	}
}

void CWaiTreeNode::setTextColor(unsigned long color)
{
	if(m_pEdit && m_pEdit->m_hWnd)
	{
		m_pEdit->setTextColor(color);
	}
}

void CWaiTreeNode::saveContent()
{
	if(!m_pNode) return;

	CString rString;
	m_pEdit->GetWindowTextW(rString);

	int nChar = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)rString, rString.GetLength(), NULL, 0, NULL, NULL);
	char* chs = new char[nChar+1];
	chs[nChar] = 0;
	WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)rString, rString.GetLength(), chs, nChar, NULL, NULL);
	
	XMLElement* elment = m_pNode->ToElement();
	elment->SetAttribute("content", chs);

	delete[] chs;

}

void CWaiTreeNode::setEditFocus()
{
	if(m_pEdit && m_pEdit->m_hWnd)
	{
		m_pEdit->SetFocus();
		int len = m_pEdit->GetWindowTextLengthW();
		m_pEdit->SetSel(len, -1);
	}
}

void CWaiTreeNode::killEditFocus()
{
	if(m_pEdit && m_pEdit->m_hWnd)
	{
		m_pEdit->SendMessage(WM_KILLFOCUS, 0, 0);
	}
}

void CWaiTreeNode::foldOrUnfold()
{
	if(!m_children.GetCount()) return;
	m_bFold = !m_bFold;
	for(int i=0; i<m_children.GetCount(); i++)
		((CWaiTreeNode*)m_children.GetAt(i))->showChildrenWindows(!m_bFold);

	CwaiView* pView = (CwaiView*)GetParent();
	pView->reloadTreeData();

}

void CWaiTreeNode::showChildrenWindows(BOOL isShow)
{
	for(int i=0; i<m_children.GetCount(); i++)
	{
		BOOL showChild = isShow;
		if(m_bFold)
			showChild = FALSE;
		((CWaiTreeNode*)m_children.GetAt(i))->showChildrenWindows(showChild);
	}
	int cmd = isShow?SW_SHOW:SW_HIDE;
	ShowWindow(cmd);
}

void CWaiTreeNode::relayout()
{
	RECT rect;
	this->GetClientRect(&rect);

	CRect rectLabel;
	m_pLabel->GetClientRect(rectLabel);
	m_pLabel->ClientToScreen(rectLabel);
	ScreenToClient(rectLabel);

	CRect rectCheckbox;
	m_pCheckBox->GetClientRect(rectCheckbox);
	m_pCheckBox->ClientToScreen(rectCheckbox);
	ScreenToClient(rectCheckbox);

	int width = rectCheckbox.Width();
	rectCheckbox.left = rectLabel.right+SPACE_HORIZON;
	rectCheckbox.right = rectCheckbox.left+width;
	m_pCheckBox->MoveWindow(rectCheckbox);

	CRect rectEdit;
	m_pEdit->GetClientRect(rectEdit);
	m_pEdit->ClientToScreen(rectEdit);
	ScreenToClient(rectEdit);

	width = rectEdit.Width();
	rectEdit.left = rectCheckbox.right;
	rectEdit.right = rectEdit.left+width;
	m_pEdit->MoveWindow(rectEdit);

	//Need to refresh it.
	UpdateWindow();
}

int CWaiTreeNode::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	RECT rect;
	this->GetClientRect(&rect);

	CRect rectLabel;
	rectLabel.bottom = rect.bottom;
	rectLabel.top = rect.top;
	rectLabel.left = 0;
	rectLabel.right = rect.left+50;
	m_pLabel = new CwaiLabel();
	m_pLabel->Create(L"1.0",WS_CHILD|WS_VISIBLE|SS_LEFT|SS_NOTIFY, rectLabel, this,GetWindowID());
	UpdateLabel();

	//-2 to elimate the effect of border width
	CRect rectCheckbox;
	rectCheckbox.bottom = rect.bottom-2;
	rectCheckbox.top = rect.top-2;
	rectCheckbox.left = rectLabel.right+SPACE_HORIZON;
	rectCheckbox.right = rectCheckbox.left+16;
	m_pCheckBox = new CWCheckbox();
	m_pCheckBox->Create(L"",WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX|
		BS_CENTER|BS_FLAT,rectCheckbox,this,GetWindowID()); 

	m_pEdit = new CWEdit(NULL, NULL, NULL, 0); 
	CRect rectEdit;
	rectEdit.top=rect.top;
	rectEdit.bottom=rect.bottom;
	rectEdit.left = rectCheckbox.right;
	rectEdit.right=rectEdit.left+250;
	
    m_pEdit->Create(WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_AUTOHSCROLL,rectEdit, this, GetWindowID());
	m_pEdit->ShowWindow(SW_SHOW);

	return 0;
}


BOOL CWaiTreeNode::OnEraseBkgnd(CDC* pDC)
{
	//Must overide this to erase background.
	CRect   m_rt;   
	GetClientRect(&m_rt);   
	CBrush   brush;   
	brush.CreateSolidBrush(RGB(255,255,255));   
	pDC->FillRect(&m_rt,&brush);  

	return CWnd::OnEraseBkgnd(pDC);
}


void CWaiTreeNode::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	setEditFocus();
	CWnd::OnLButtonDown(nFlags, point);
}
